#include "mpCodecUtils.h"
#include "openssl/evp.h"


namespace MP{
namespace CodecUtils{

//private member
namespace{

const std::string _base64_chars =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz"
        "0123456789+/";

bool _is_base64(unsigned char c) {
    return (isalnum(c) || (c == '+') || (c == '/'));
}

unsigned char _charToHex(unsigned char x) {
    return (unsigned char) (x > 9 ? x + 55 : x + 48);
}

bool _isAlphaNumber(unsigned char c) {
    if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
        return true;
    return false;
}

} //private member



/**
 * @brief 字符base64编码
 * @param input
 * @param bytes
 * @param output
 */
void base64_encode(const unsigned char * input,int bytes, std::string & output){
    output.clear();


    int i = 0;
    int j = 0;
    unsigned char char_array_3[3];  // store 3 byte of bytes_to_encode
    unsigned char char_array_4[4];  // store encoded character to 4 bytes

    while (bytes--) {
        char_array_3[i++] = *(input++);  // get three bytes (24 bits)
        if (i == 3) {
            // eg. we have 3 bytes as ( 0100 1101, 0110 0001, 0110 1110) --> (010011, 010110, 000101, 101110)
            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; // get first 6 bits of first byte,
            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); // get last 2 bits of first byte and first 4 bit of second byte
            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); // get last 4 bits of second byte and first 2 bits of third byte
            char_array_4[3] = char_array_3[2] & 0x3f; // get last 6 bits of third byte

            for (i = 0; (i < 4); i++)
                output += _base64_chars[char_array_4[i]];
            i = 0;
        }
    }

    if (i)
    {
        for (j = i; j < 3; j++)
            char_array_3[j] = '\0';

        char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);

        for (j = 0; (j < i + 1); j++)
            output += _base64_chars[char_array_4[j]];

        while ((i++ < 3))
            output += '=';

    }

}

/**
 * @brief 字符base64解码
 * @param input
 * @param length
 * @param output
 */
void base64_decode(const std::string& input,std::string & output){
    output.clear();


    size_t in_len = input.size();
    int i = 0;
    int j = 0;
    int in_ = 0;
    unsigned char char_array_4[4], char_array_3[3];


    while (in_len-- && (input[in_] != '=') && _is_base64(input[in_])) {
        char_array_4[i++] = input[in_]; in_++;
        if (i == 4) {
            for (i = 0; i < 4; i++)
                char_array_4[i] = _base64_chars.find(char_array_4[i]) & 0xff;

            char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
            char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
            char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

            for (i = 0; (i < 3); i++)
                output += char_array_3[i];
            i = 0;
        }
    }

    if (i) {
        for (j = 0; j < i; j++)
            char_array_4[j] = _base64_chars.find(char_array_4[j]) & 0xff;

        char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
        char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);

        for (j = 0; (j < i - 1); j++) output += char_array_3[j];
    }



}

/**
 * @brief codec_des_ecb_encode
 * @param input
 * @param key
 * @param output_base64
 */
void des_ecb_encode(const std::string & input,const std::string &key, std::string & output_base64){
    output_base64.clear();

    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init(&ctx);
    int ret = EVP_EncryptInit_ex(&ctx, EVP_des_ecb(), NULL, (unsigned char*)key.data(), NULL);
    if (ret != 1){
        return;
    }

    unsigned char* result = new unsigned char[input.length() + 64]; // 弄个足够大的空间

    {
        int len1 = 0;
        ret = EVP_EncryptUpdate(&ctx, result, &len1, (unsigned char*)input.data(), input.length());
        if (ret != 1){
            return;
        }

        int len2 = 0;
        ret = EVP_EncryptFinal_ex(&ctx, result+len1, &len2);
        if (ret != 1){
            return;
        }


        ret = EVP_CIPHER_CTX_cleanup(&ctx);
        if (ret != 1){
            return;
        }

        //返回base64编码的字符串
        base64_encode(result,len1+len2,output_base64);
    }


    delete[] result;
}

/**
 * @brief codec_des_ecb_decode
 * @param input_base64
 * @param key
 * @param output
 */
void des_ecb_decode(const std::string & input_base64,const std::string &key, std::string & output){
    output.clear();

    //先将字符串执行base64解码
    std::string normal;
    base64_decode(input_base64,normal);

    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init(&ctx);
    int ret = EVP_DecryptInit_ex(&ctx, EVP_des_ecb(), NULL, (unsigned char*)key.data(), NULL);
    if (ret != 1){
        return;
    }

    unsigned char* result = new unsigned char[normal.length() + 64]; // 弄个足够大的空间

    {

        int len1 = 0;
        ret = EVP_DecryptUpdate(&ctx, result, &len1, (unsigned char*)normal.data(), normal.length());
        if (ret != 1){
            return;
        }

        int len2 = 0;
        ret = EVP_DecryptFinal_ex(&ctx, result+len1, &len2);
        if (ret != 1){
            return;
        }

        ret = EVP_CIPHER_CTX_cleanup(&ctx);
        if (ret != 1){
            return;
        }

        output.append((char*)result, len1+len2);

    }

    delete[] result;

}


/**
* @ 将UTF8格式的字符串做URL编码
* @param pOut 加密后的结果
* @param text 输入文本
*/
void utf8Url_encode(std::string& pOut,const std::string & text){

    //参考：https://www.jianshu.com/p/234271dfd95e
    pOut.clear();
    unsigned char *p = (unsigned char *) text.c_str();
    unsigned char ch;
    while (*p) {
        ch = (unsigned char) *p;
        if (*p == ' ') {
            pOut += '+';
        //} else if (_isAlphaNumber(ch) || strchr("-_.~!*'();:@&=+$,/?#[]", ch)) {
        } else if (_isAlphaNumber(ch) || strchr("-_.~!*'();:@&=$,/?#[]", ch)) {
            pOut += *p;
        } else {
            pOut += '%';
            pOut += _charToHex((unsigned char) (ch >> 4));
            pOut += _charToHex((unsigned char) (ch % 16));
        }
        ++p;
    }
}

/**
* @ 将UTF8格式的字符串做URL解码
* @param pOut 加密后的结果
* @param text 输入文本
*/
void utf8Url_decode(std::string& pOut,const std::string & text){

    //参考：https://www.jianshu.com/p/234271dfd95e
    pOut.clear();

    char *cd = (char *) text.c_str();
    char p[2];
    for (std::size_t i = 0; i < strlen(cd); i++) {
        memset(p, '\0', 2);
        if (cd[i] != '%') {
            pOut += cd[i];
            continue;
        }
        p[0] = cd[++i];
        p[1] = cd[++i];
        p[0] = p[0] - 48 - ((p[0] >= 'A') ? 7 : 0) - ((p[0] >= 'a') ? 32 : 0);
        p[1] = p[1] - 48 - ((p[1] >= 'A') ? 7 : 0) - ((p[1] >= 'a') ? 32 : 0);
        pOut += (unsigned char) (p[0] * 16 + p[1]);
    }

}


}   //END CodecUtils
}   //END MP





